package com.hhck.service.impl;

import com.hhck.constants.Constants;
import com.hhck.entity.Page;
import com.hhck.entity.Result;
import com.hhck.entity.Role;
import com.hhck.mapper.AuthMapper;
import com.hhck.mapper.RoleAuthMapper;
import com.hhck.mapper.RoleMapper;
import com.hhck.mapper.UserRoleMapper;
import com.hhck.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;

// 注解版缓存第二步：指定缓存的名称（数据保存到redis中key的前缀，一般是标注注解类的全路径）
@CacheConfig(cacheNames = "com.hhck.service.RoleServiceImpl")
@Service
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RoleAuthMapper roleAuthMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private AuthMapper authMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 查询所有角色（已启用）
     */
    // 注解版缓存第三步：查询方法上标注解，指定缓存的key
    @Cacheable(key = "'all:role'")
    @Override
    public List<Role> queryAllRole() {
        return roleMapper.selectAllRole();
    }

    /**
     * 分页查询所有角色（条件查询）
     */
    @Override
    public Page queryRoleByPage(Page page, Role role) {

        // 查询角色分页信息
        List<Role> roleList = roleMapper.selectRoleByPage(page, role);

        // 查询角色信息总行数（条件查询）
        int count = roleMapper.selectRoleCount(role);

        // 设置总行数
        page.setTotalNum(count);
        // 设置总页数
        page.setPageCount(page.getPageCount());
        // 将角色信息封装到分页对象中
        page.setResultList(roleList);

        return page;
    }

    /**
     * 添加角色
     */
    // 注解版缓存：该方法执行后会触发缓存的清除操作，保证缓存的一致性和准确性
    @CacheEvict(key = "'all:role'")
    @Override
    public Result saveRole(Role role) {

        // 首先判断要添加的角色是否已经存在
        Role isExist = roleMapper.selectRoleByRoleNameOrRoleCode(role.getRoleName(), role.getRoleCode());
        if (isExist != null) {
            // 角色已存在
            return Result.err(Result.CODE_ERR_BUSINESS, "添加失败，当前名称或代码已存在！");
        }
        // 程序执行到这里说明角色名称合法

        int num = roleMapper.insertRole(role);
        if (num == 1) {
            return Result.ok("添加成功！");
        }
        return Result.err(Result.CODE_ERR_BUSINESS, "添加失败！");
    }

    /**
     * 根据角色id启用或禁用角色
     */
    // 注解版缓存：该方法执行后会触发缓存的清除操作，保证缓存的一致性和准确性
    @CacheEvict(key = "'all:role'")
    @Override
    public Result modifyRoleStateByRoleId(Role role) {

        // 角色状态发生变更，清除用户权限菜单树缓存
        clearAllUserAuthCache();

        // 更改状态
        int num = roleMapper.updateRoleStateByRoleId(role);
        if (num == 1) {
            return Result.ok("状态更改成功！");
        }
        return Result.err(Result.CODE_ERR_BUSINESS, "状态更改失败！");
    }

    /**
     * 根据角色id删除角色
     */
    // 注解版缓存：该方法执行后会触发缓存的清除操作，保证缓存的一致性和准确性
    @CacheEvict(key = "'all:role'")
    // 开启事务
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeRoleByRoleId(Integer roleId) {

        // 删除角色
        roleMapper.deleteRoleByRoleId(roleId);

        // 删除角色权限关系（可以在设计表结构的时候设置外键级联删除，这样就不用调用其他方法删除了，但不推荐级联删除）
        roleAuthMapper.deleteRoleAuthByRoleId(roleId);

        // 删除用户角色关系（可以在设计表结构的时候设置外键级联删除，这样就不用调用其他方法删除了，但不推荐级联删除）
        userRoleMapper.deleteUserRoleByRoleId(roleId);

        // 角色被删除，清除用户权限菜单树缓存
        clearAllUserAuthCache();
    }

    /**
     * 根据角色id查询权限id
     */
    @Override
    public List<Integer> queryRoleAuthIdListByRoleId(Integer roleId) {
        return authMapper.selectAuthIdListByRoleId(roleId);
    }

    /**
     * 根据角色id给角色分配权限
     */
    // 开启事务
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveRoleAuthByRoleId(Integer roleId, List<Integer> authIdList) {

        // 1、根据角色id删除角色所有权限
        roleAuthMapper.deleteRoleAuthByRoleId(roleId);

        // 2、根据角色id给角色分配权限
        roleAuthMapper.insertRoleAuthByRoleId(roleId, authIdList);

        // 角色权限发生变化，清除与该角色相关用户的权限缓存
        clearUserAuthCache(roleId);
    }

    /**
     * 根据角色id修改角色描述信息
     */
    // 角色信息发生变更，清除缓存
    @CacheEvict(key = "'all:role'")
    @Override
    public Result updateRoleDescByRoleId(Role role) {

        int num = roleMapper.updateRoleDescByRoleId(role);
        if (num == 1) {
            return Result.ok("修改成功！");
        }
        return Result.err(Result.CODE_ERR_BUSINESS, "修改失败！");
    }

    /**
     * 清除用户缓存（当角色的权限或者状态发生变更时，需要清除权限缓存，保证数据一致）
     */
    public void clearUserAuthCache(Integer roleId) {

        // 获取所有和该角色相关用户的id，然后遍历删除这些用户的缓存，保证数据一致，而不影响其他无关用户
        List<Integer> userIdList = userRoleMapper.selectUserIdListByRoleId(roleId);

        // 遍历删除指定用户权限缓存
        for (Integer userId : userIdList) {
            redisTemplate.delete(Constants.AUTH_TREE_PREFIX + userId);
        }
    }

    /**
     * 清除所有用户缓存（当角色的权限或者状态发生变更时，需要清除权限缓存，保证数据一致）
     */
    public void clearAllUserAuthCache() {

        // 获取指定前缀的key（获取所有用户的权限缓存）
        Set<String> keys = redisTemplate.keys(Constants.AUTH_TREE_PREFIX + "*");

        // 遍历删除所有用户权限缓存
        for (String key : keys) {
            redisTemplate.delete(key);
        }
    }

}

